#include <algorithm>

#include <boost/foreach.hpp>

#include "CompilerVisitor.h"
#include "Exceptions.h"

using namespace std;
using namespace boost;

CompilerVisitor::CompilerVisitor(const ParseHelper& helper)
 : classes(helper.fAllClassDefinitions),
   functions(helper.fAllFunctionDefinitions),
   fStringConstants(helper.GetStringConstants()),
   fEntryPoint(helper.GetEntryPoint()),
   fCalledFunctionNames(helper.GetCalledFunctionNames()),
   fIsInConstructorContext(false)
{
    FillStringPool();
    fWholeCode.reserve(1024 * 1024);
}

CompilerVisitor::~CompilerVisitor()
{
}

void CompilerVisitor::FillStringPool()
{
	BOOST_FOREACH(string* s, fStringConstants)
	{
		pool.push_back(*s);
	}

	BOOST_FOREACH(ClassDefinition * c, classes)
	{
		pool.push_back(c->GetClassName());
		vector<MethodDefinition *> methods = c->GetAllMethods();
        vector<FieldDeclaration*> lFields = c->GetAllFields();

		BOOST_FOREACH(MethodDefinition *& m, methods)
        {
			pool.push_back(m->GetName());
        }

        BOOST_FOREACH(FieldDeclaration* lField, lFields)
        {
            pool.push_back(lField->GetVarName());
        }
	}

	BOOST_FOREACH(FunctionDefinition * f, functions)
	{
        pool.push_back(f->GetName());
	}

	BOOST_FOREACH(string s, fCalledFunctionNames)
	{
        pool.push_back(s);
	}

	sort(pool.begin(), pool.end());
	pool.erase(unique(pool.begin(), pool.end()), pool.end());
}

void CompilerVisitor::Visit(ArrayAccess* iNode)
{
    iNode->GetIndexExpression()->Accept(this);
    iNode->GetArrayRef()->Accept(this);

    if(iNode->IsLeftValue())
        Generate(SETARRAYVALUE);
    else
        Generate(GETARRAYVALUE);
}

void CompilerVisitor::Visit(BinaryOperation* iNode)
{
    // nejdriv pravy, pak levy, aby levy byl na vrcholu zasobniku
	iNode->GetRight()->Accept(this);
	iNode->GetLeft()->Accept(this);

    BinaryOperationType lOp = iNode->GetOperation();

	switch (lOp)
	{
	    case Plus:
		    Generate(ADD);
		    break;
	    case Minus:
		    Generate(SUB);
		    break;
	    case Multiply:
		    Generate(MUL);
		    break;
	    case Divide:
		    Generate(DIV);
		    break;
	    case Modulo:
		    Generate(MOD);
		    break;
	    case And:
		    Generate(AND);
		    break;
	    case Or:
		    Generate(OR);
		    break;
	    case Equal:
		    Generate(EQL);
		    break;
	    case NotEqual:
		    Generate(NEQL);
		    break;
	    case Less:
		    Generate(LT);
		    break;
	    case Greater:
		    Generate(GT);
		    break;
	    case LessOrEqual:
		    Generate(LE);
		    break;
	    case GreaterOrEqual:
		    Generate(GE);
		    break;
        default:
            throw UnsupportedBinaryOperationType(lOp);
	}
}

void CompilerVisitor::Visit(ConstantValue* iNode)
{
	switch (iNode->fType)
	{
	    case LongInteger:
            Generate(ICONST, (int) iNode->fValue.LongValue);
		    break;
        case String:
            Generate(SCONST, GetNameIndex(*(iNode->fValue.StringValue)));
            break;
	    default:
            throw UnsupportedConstantValueType(iNode->fType);
	}
}

void CompilerVisitor::Visit(FieldAccess* iNode)
{
    iNode->GetObjectRef()->Accept(this);

    if(iNode->IsLeftValue())
        Generate(SETFIELDVALUE, GetNameIndex(iNode->GetFieldName()));
    else
        Generate(GETFIELDVALUE, GetNameIndex(iNode->GetFieldName()));
}

void CompilerVisitor::Visit(UnaryOperation* iNode)
{
    iNode->GetExpression()->Accept(this);

    UnaryOperationType lOp = iNode->GetOperation();

    switch(lOp)
    {
        case UnaryMinus:
            Generate(MINUS);
            break;
        case Negation:
            Generate(NEG);
            break;
        default:
            throw UnsupportedUnaryOperationType(lOp);
    }
}

void CompilerVisitor::Visit(Variable* iNode)
{
    if(iNode->IsLeftValue())
        Generate(SETLOCAL, fLocalVars[iNode->GetVarName()]);
    else
        Generate(GETLOCAL, fLocalVars[iNode->GetVarName()]);
}

void CompilerVisitor::Visit(Assign* iNode)
{
    iNode->GetExpression()->Accept(this);
    iNode->GetTarget()->Accept(this);
}

void CompilerVisitor::Visit(EmptyStatement*)
{
    // je to praaazdny :-)
}

void CompilerVisitor::Visit(ForLoop* iNode)
{
    /*
    initial-assign
                         <---+
    condition                |
    if-not-ok-jump-here      |
    | body                   |
    | after-body-assignment  |
    | jump-here--------------+
    +-->
    */

    TreeNode* lCondition = iNode->GetCondition();
    TreeNode* lBody = iNode->GetBody();
    TreeNode* lInitialAssign = iNode->GetInitialAssign();
    TreeNode* lAfterBodyAssign = iNode->GetAfterBodyAssignment();

    lInitialAssign->Accept(this);

    unsigned int lPos, lCurrSize, lBlockSize;
    int lJumpSize;

    unsigned int lCondStart = bytecode.size(); // adresa, kam se bude skakat po dokonceni cyklu

    lCondition->Accept(this);

    lPos = bytecode.size() + 1; // pozice, kde bude zacinat offset skoku
    Generate(JF, 0xFFFFFFFF);
    lCurrSize = bytecode.size();

    lBody->Accept(this);

    lAfterBodyAssign->Accept(this);

    lJumpSize = bytecode.size() + 5 - lCondStart; // kolik musim preskocit zpet po provedeni JMP abych se dostal na podminku

    Generate(JMP, -lJumpSize);

    lBlockSize = bytecode.size() - lCurrSize;
    ReplaceInt(bytecode, lPos, lBlockSize);
}

void CompilerVisitor::Visit(IfStatement* iNode)
{
    TreeNode* lCond = iNode->GetCondition();
    TreeNode* lThen = iNode->GetThenPart();
    TreeNode* lElse = iNode->GetElsePart();

    lCond->Accept(this);

    if(lElse == 0)
    {
        /*
        if-not-ok-jump-here
        |   <t�lo-if>
        +->
        */
        unsigned int lOffsetPosition, lCurrSize, lBodySize;

        lOffsetPosition = bytecode.size() + 1;

        Generate(JF, 0xFFFFFFFF);

        lCurrSize = bytecode.size();
        lThen->Accept(this);
        lBodySize = bytecode.size() - lCurrSize;

        ReplaceInt(bytecode, lOffsetPosition, lBodySize);
    }
    else
    {
        /*
        if-not-ok-jump-here
        |   <t�lo-if>
        |   <jump-here>--+
        +-> <t�lo-else>  |
                       <-+
        */
        unsigned int lCurrSize, lPos1, lPos2, lThenSize, lElseSize;

        lPos1 = bytecode.size() + 1; // pozice offsetu v JF
        Generate(JF, 0xFFFFFFFF);

        lCurrSize = bytecode.size();
        lThen->Accept(this);
        lPos2 = bytecode.size() + 1; // pozice offsetu v JMP
        Generate(JMP, 0xFFFFFFFF);
        lThenSize = bytecode.size() - lCurrSize;

        ReplaceInt(bytecode, lPos1, lThenSize);

        lCurrSize = bytecode.size();
        lElse->Accept(this);
        lElseSize = bytecode.size() - lCurrSize;

        ReplaceInt(bytecode, lPos2, lElseSize);
    }
}

void CompilerVisitor::Visit(NewExpression* iNode)
{
    // nejdriv parametry konsturktoru
    vector<TreeNode*> lParams = iNode->GetConstructorParams();

    BOOST_FOREACH(TreeNode* lParam, lParams)
    {
        lParam->Accept(this);
    }
                                                                   // + 2 = this a usetrena +1 ve vm
    Generate(NEW, GetNameIndex(iNode->GetClassName()), lParams.size() + 2);
}

void CompilerVisitor::Visit(ReturnStatement* iNode)
{
    // konstruktor musi vzdy vracet "this"... toto je fix pro situace, kdyz tam zly programator vraci cokoliv jineho
    if(fIsInConstructorContext)
    {
        Variable* lVar = new Variable("this");
        lVar->Accept(this);
        Generate(RETVALUE);
        return;
    }

    TreeNode* lRetExpression = iNode->GetReturnExpression();

    if(lRetExpression == 0)
    {
        Generate(RET);
    }
    else
    {
        lRetExpression->Accept(this);
        Generate(RETVALUE);
    }
}

void CompilerVisitor::Visit(StatementList* iNode)
{
    vector<TreeNode*> lStmts = iNode->GetStatements();

	BOOST_FOREACH(TreeNode* n, lStmts)
    {
		n->Accept(this);
    }
}

void CompilerVisitor::Visit(VarDeclaration* iNode)
{
    // jedine co je potreba tady generovat je pripadne prirazeni hodnoty do promenne
    TreeNode* lInitialValue = iNode->GetInitialValue();

    if(lInitialValue != 0)
    {
        lInitialValue->Accept(this);
        Generate(SETLOCAL, fLocalVars[iNode->GetVarName()]);
    }
}

void CompilerVisitor::Visit(WhileLoop* iNode)
{
    /*
                          <--+
    podm�nka                 |
    if-not-ok-then-jump-here |
    | <t�lo>                 |
    | <jump-here>------------+
    +-->
    */
    TreeNode* lCondition = iNode->GetCondition();
    TreeNode* lBody = iNode->GetBody();

    unsigned int lPos1, lCurrSize, lBlockSize;
    unsigned int lCondStart = bytecode.size();
    int lJumpSize;

    lCondition->Accept(this);
    lPos1 = bytecode.size() + 1;
    Generate(JF, 0xFFFFFFFF);
    lCurrSize = bytecode.size();
    lBody->Accept(this);

    // co�e? :-) podle specifikace se PC po zpracov�n� instrukce implicitn� posune na dal�� instrukci
    // a voliteln� se provede dal�� posun
    // �ili po zpracov�n� JMP se posune na instrukci n�sleduj�c� za JMP, tak�e do posunu mus�m zapo��tat i JMP (5 byt�)
    // a offset mus�m ud�lat z�porn�, abych sko�il zp�t a ne dop�edu
    lJumpSize = bytecode.size() + 5 - lCondStart;
    Generate(JMP, -lJumpSize);
    lBlockSize = bytecode.size() - lCurrSize;
    ReplaceInt(bytecode, lPos1, lBlockSize);
}

void CompilerVisitor::Visit(FunctionCall* iNode)
{
    vector<TreeNode*> lParams = iNode->GetCallParams();

    BOOST_FOREACH(TreeNode* lParam, lParams)
    {
        lParam->Accept(this);
    }

    if(iNode->GetDefinition() == 0 || iNode->GetDefinition()->IsBuiltIn())
    {
        Generate(ECALL, GetNameIndex(iNode->GetFunctionName()), lParams.size());
    }
    else
    {
        // prvni parametr = adresa v bytekodu
        // bytecode.size() + 1 je index do bytecode, kam se pozdeji vlozi adresa funkce
        fFuncAddrBackPatches.push_back(make_pair(bytecode.size() + 1, iNode->GetDefinition()));

        // adresu ale muzeme zjistit az na konci, proto tam ted dame nejaky rozumny nesmysl a na konci ho nahradime spravnou adresou
        Generate(FCALL, 0xFFFFFFFF, lParams.size() + 1);
    }
}

void CompilerVisitor::Visit(MethodCall* iNode)
{
    vector<TreeNode*> lParams = iNode->GetCallParams();
    TreeNode* lObjectRef = iNode->GetObjectRef();

    BOOST_FOREACH(TreeNode* lParam, lParams)
    {
        lParam->Accept(this);
    }

    lObjectRef->Accept(this);                                         // + 2 = this a usetrena +1 ve vm
    Generate(MCALL, GetNameIndex(iNode->GetMethodName()), lParams.size() + 2);
}

void CompilerVisitor::Visit(ClassDefinition*)
{
}

void CompilerVisitor::Visit(FunctionDefinition* iNode)
{
    // posledni parametr bude na pozici -2 (relativne vuci FP), predposledni -3, ...
    // lokalni promenne budou zacinat na pozici 1
    vector<string> lParams = iNode->GetInputVarNames();
    vector<string> lLocals = iNode->GetLocalVarNames();

    signed char lPosition = -2;

    BOOST_REVERSE_FOREACH(string lName, lParams)
    {
        fLocalVars[lName] = lPosition;
        lPosition--;
    }

    lPosition = 1;

    BOOST_FOREACH(string lName, lLocals)
    {
        fLocalVars[lName] = lPosition;
        lPosition++;
    }

    Generate(FRAME, iNode->GetLocalsCount());
    iNode->GetBody()->Accept(this);

    Generate(RET); // implicitn� return - pro jistotu, pokud v definici nen�
    fLocalVars.clear();
}

void CompilerVisitor::Visit(MethodDefinition* iNode)
{
    // posledni parametr bude na pozici -3 (relativne vuci FP), predposledni -4, ...
    // na pozici -2 bude this
    // lokalni promenne budou zacinat na pozici 1
    vector<string> lParams = iNode->GetInputVarNames();
    vector<string> lLocals = iNode->GetLocalVarNames();

    signed char lPosition = -2;

    fLocalVars["this"] = lPosition;

    lPosition--;

    BOOST_REVERSE_FOREACH(string lName, lParams)
    {
        fLocalVars[lName] = lPosition;
        lPosition--;
    }

    lPosition = 1;

    BOOST_FOREACH(string lName, lLocals)
    {
        fLocalVars[lName] = lPosition;
        lPosition++;
    }

    Generate(FRAME, iNode->GetLocalsCount());

    if(iNode->IsConstructor())
        fIsInConstructorContext = true;

    iNode->GetBody()->Accept(this);

    // konstruktor musi vzdy vracet this... toto je fix pro situace, kdy v tele zadny return neni - prida se tam na konec
    if(iNode->IsConstructor())
    {
        Variable* lThis = new Variable("this");
        lThis->Accept(this);
        Generate(RETVALUE);
        fIsInConstructorContext = false;
    }
    else
        Generate(RET); // implicitn� return - pro jistotu, pokud v definici nen�

    fLocalVars.clear();
}

void CompilerVisitor::Generate( Instruction i, int arg1 /*= 0*/, int arg2 /*= 0*/ )
{
	bytecode.push_back(static_cast<unsigned char>(i));

	switch (i)
	{
	    case MCALL:
            AppendUnsignedAsLittleEndian(arg1, bytecode); // POOL_INDEX
            bytecode.push_back(static_cast<unsigned char>(arg2)); // CALL_PARAM_COUNT
    		break;

	    case FCALL:
            AppendUnsignedAsLittleEndian(arg1, bytecode); // OFFSET
            bytecode.push_back(static_cast<unsigned char>(arg2)); // CALL_PARAM_COUNT
    		break;

	    case ECALL:
            AppendUnsignedAsLittleEndian(arg1, bytecode); // POOL_INDEX
            bytecode.push_back(static_cast<unsigned char>(arg2)); // CALL_PARAM_COUNT
    		break;

        case ICONST:
            AppendSignedAsLittleEndian(arg1, bytecode); // INTCONST
            break;

        case SCONST:
            AppendUnsignedAsLittleEndian(arg1, bytecode); // POOL_INDEX
            break;

        case SETLOCAL:
        case GETLOCAL:
            bytecode.push_back(static_cast<signed char>(arg1)); // INDEX
            break;

        case SETFIELDVALUE:
        case GETFIELDVALUE:
            AppendUnsignedAsLittleEndian(arg1, bytecode); // POOL_INDEX
            break;

        case NEW:
            AppendUnsignedAsLittleEndian(arg1, bytecode); // POOL_INDEX
            bytecode.push_back(static_cast<unsigned char>(arg2)); // CALL_PARAM_COUNT
            break;

        case FRAME:
            bytecode.push_back(static_cast<unsigned char>(arg1)); // LOCALS_COUNT
            break;

        case JF:
        case JT:
        case JMP:
            AppendSignedAsLittleEndian(arg1, bytecode); // OFFSET
            break;

	    default:;
	}
}

unsigned int CompilerVisitor::GetNameIndex(const string& name) const
{
	StringPool::const_iterator i = lower_bound(pool.begin(), pool.end(), name);

	if (i != pool.end())
	{
		return i - pool.begin();
	}

    throw BaseException("GetNameIndex: '" + name + "' nebylo nalezeno!");
}

void CompilerVisitor::Serialize(ostream& out)
{
	// STRING_POOL
    OutputStringPool();

	// FUNCTIONS
    OutputFunctions();

	// CLASSES
    OutputClasses();

    ProcessBackPatches();

    // ENTRY_POINT
    AppendUnsignedAsLittleEndian(fEntryPoint->GetBytecodeOffset(), fWholeCode);

	// BYTECODE
    fWholeCode.insert(fWholeCode.end(), bytecode.begin(), bytecode.end());

	fWholeCode.insert(fWholeCode.begin(), 0xE5);
	fWholeCode.insert(fWholeCode.begin(), 0xB1);
	fWholeCode.insert(fWholeCode.begin(), 0x00);
	fWholeCode.insert(fWholeCode.begin(), 0x0B);

	out.write(&(fWholeCode[0]), fWholeCode.size());
}

void CompilerVisitor::OutputStringPool()
{
	BOOST_FOREACH(string s, pool)
	{
        AppendUnsignedAsLittleEndian(s.length(), fWholeCode);
        fWholeCode.insert(fWholeCode.end(), s.begin(), s.end());
	}

    int lSize = fWholeCode.size();

    vector<char> lPoolLengthBytes = Util::Int2LittleEndian(lSize);
    fWholeCode.insert(fWholeCode.begin(), lPoolLengthBytes.begin(), lPoolLengthBytes.end());
}

void CompilerVisitor::OutputFunctions()
{
    vector<char> lAllFunctionDefinitions;
    vector<char> lOneDefinition;

	BOOST_FOREACH(FunctionDefinition* lFunction, functions)
	{
        // postav definici jedne funkce; velikost vyresime potom
        AppendUnsignedAsLittleEndian(GetNameIndex(lFunction->GetName()), lOneDefinition);
        lOneDefinition.push_back(lFunction->IsBuiltIn());

        // pointer do bytekodu je na konec - na konci smycky tam pridam kod tehle funkce
        AppendUnsignedAsLittleEndian(bytecode.size(), lOneDefinition);
        // params + 1 je trapna optimalizace pro vm - nemusi pri RETVAL tu 1 znovu pricitat
        AppendUnsignedAsLittleEndian(lFunction->GetParamCount() + 1, lOneDefinition);
        AppendUnsignedAsLittleEndian(lFunction->GetLocalsCount(), lOneDefinition);

        // ted to pridej do definic vsech funkci
        // zacni delkou definice teto funkce
        AppendUnsignedAsLittleEndian(lOneDefinition.size(), lAllFunctionDefinitions);
        // a pridej telo definice
        lAllFunctionDefinitions.insert(lAllFunctionDefinitions.end(), lOneDefinition.begin(), lOneDefinition.end());

        // a vymaz to
        lOneDefinition.clear();

        lFunction->SetBytecodeOffset(bytecode.size());

        // a ted vygeneruj kod!
        lFunction->Accept(this);
	}

    // definice vsech funkci pridej do vysledku
    AppendUnsignedAsLittleEndian(lAllFunctionDefinitions.size(), fWholeCode);
    fWholeCode.insert(fWholeCode.end(), lAllFunctionDefinitions.begin(), lAllFunctionDefinitions.end());
}

void CompilerVisitor::OutputClasses()
{
    vector<char> lAllClassDefinitions;
    vector<char> lOneClassDefinition;

    BOOST_FOREACH(ClassDefinition* lClass, classes)
    {
        BuildClassDefinition(lClass, lOneClassDefinition);
        AppendUnsignedAsLittleEndian(lOneClassDefinition.size(), lAllClassDefinitions);
        lAllClassDefinitions.insert(lAllClassDefinitions.end(), lOneClassDefinition.begin(), lOneClassDefinition.end());
        lOneClassDefinition.clear();
    }

    AppendUnsignedAsLittleEndian(lAllClassDefinitions.size(), fWholeCode);
    fWholeCode.insert(fWholeCode.end(), lAllClassDefinitions.begin(), lAllClassDefinitions.end());
}

void CompilerVisitor::BuildClassDefinition(ClassDefinition* iClass, vector<char>& oTarget)
{
    vector<char> lMethodDefs;
    vector<char> lFieldDefs;

    AppendUnsignedAsLittleEndian(GetNameIndex(iClass->GetClassName()), oTarget);

    BuildFieldDefinitions(iClass, lFieldDefs);
    BuildMethodDefinitions(iClass, lMethodDefs);

    oTarget.insert(oTarget.end(), lFieldDefs.begin(), lFieldDefs.end());
    oTarget.insert(oTarget.end(), lMethodDefs.begin(), lMethodDefs.end());
}

void CompilerVisitor::BuildFieldDefinitions(ClassDefinition* iClass, vector<char>& oTarget)
{
    vector<char> lFieldsBuffer;
    vector<FieldDeclaration*> lFields = iClass->GetAllFields();

    BOOST_FOREACH(FieldDeclaration* lField, lFields)
    {
        AppendUnsignedAsLittleEndian(GetNameIndex(lField->GetVarName()), lFieldsBuffer);
    }

    AppendUnsignedAsLittleEndian(lFieldsBuffer.size(), oTarget);
    oTarget.insert(oTarget.end(), lFieldsBuffer.begin(), lFieldsBuffer.end());
}

void CompilerVisitor::BuildMethodDefinitions(ClassDefinition* iClass, vector<char>& oTarget)
{
    vector<char> lAllMethodDefinitions;
    vector<char> lOneMethodDefinition;
    vector<MethodDefinition*> lMethods = iClass->GetAllMethods();

    BOOST_FOREACH(MethodDefinition* lMethod, lMethods)
    {
        // postav definici jedne metody; velikost vyresime potom
        AppendUnsignedAsLittleEndian(GetNameIndex(lMethod->GetName()), lOneMethodDefinition);
        AppendUnsignedAsLittleEndian(bytecode.size(), lOneMethodDefinition);
        // params + 2 je +1 pro this a +1 optimalizace pro vm - nemusi pri RETVAL tu 1 znovu pricitat
        AppendUnsignedAsLittleEndian(lMethod->GetParamCount() + 2, lOneMethodDefinition);
        AppendUnsignedAsLittleEndian(lMethod->GetLocalsCount(), lOneMethodDefinition);

        // ted to pridej do definic vsech metod
        // zacni delkou definice teto metody
        AppendUnsignedAsLittleEndian(lOneMethodDefinition.size(), lAllMethodDefinitions);
        // a pridej telo definice
        lAllMethodDefinitions.insert(lAllMethodDefinitions.end(), lOneMethodDefinition.begin(), lOneMethodDefinition.end());

        // a vymaz to
        lOneMethodDefinition.clear();

        lMethod->SetBytecodeOffset(bytecode.size());

        // a ted vygeneruj kod!
        lMethod->Accept(this);
    }

    AppendUnsignedAsLittleEndian(lAllMethodDefinitions.size(), oTarget);
    oTarget.insert(oTarget.end(), lAllMethodDefinitions.begin(), lAllMethodDefinitions.end());
}

void CompilerVisitor::ProcessBackPatches()
{
    // pri volani funkce se pouziva primo adresa funkce v bytekodu - tu ale pri prekladu nezname a muzeme ji doplnit az ted
    unsigned int lSize = fFuncAddrBackPatches.size();

    for(unsigned int i = 0; i < lSize; i++)
    {
        unsigned int lCallAddress = fFuncAddrBackPatches[i].first;
        unsigned int lCallTarget = fFuncAddrBackPatches[i].second->GetBytecodeOffset();

        vector<char> lRealAddress = Util::Int2LittleEndian(lCallTarget);

        for(unsigned int j = 0; j < 4; j++)
        {
            bytecode[lCallAddress + j] = lRealAddress[j];
        }
    }
}

void CompilerVisitor::AppendUnsignedAsLittleEndian(unsigned int iValue, vector<char>& oTarget) const
{
    vector<char> lValueBytes = Util::Int2LittleEndian(iValue);
    oTarget.insert(oTarget.end(), lValueBytes.begin(), lValueBytes.end());
}

void CompilerVisitor::AppendSignedAsLittleEndian(int iValue, vector<char>& oTarget) const
{
    vector<char> lValueBytes = Util::Int2LittleEndian(iValue);
    oTarget.insert(oTarget.end(), lValueBytes.begin(), lValueBytes.end());
}

void CompilerVisitor::ReplaceInt(vector<char>& oWhere, unsigned int iStart, int iReplacement)
{
    vector<char> lReplacement = Util::Int2LittleEndian(iReplacement);

    for(int i = 0; i < 4; i++)
    {
        oWhere[i + iStart] = lReplacement[i];
    }
}
